/*****************************************************************************
 * $LastChangedDate: 2011-09-10 23:54:34 -0400 (Sat, 10 Sep 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Math functions (trig).  See math/funcs.hh.
 * REMINDERS: Consider using MATH_EXCEPTION() instead of ASSERT().
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef MATH_FUNCS_TRIG_HH
#define MATH_FUNCS_TRIG_HH 1

#include <utility>
#include "math/defs_trig.hh"
#include "math/types_trig.hh"
#include "math/funcs_debug.hh"
#include "math/funcs.hh"

namespace math {

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  T R I G   /////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Compare degrees/radians while ignoring sign.
#define IF_DEG_GT( DEG1, DEG2 ) (ABS(DEG1) > ABS(DEG2))
#define IF_DEG_LT( DEG1, DEG2 ) (ABS(DEG1) < ABS(DEG2))
#define IF_RAD_GT( RAD1, RAD2 ) (ABS(RAD1) > ABS(RAD2))
#define IF_RAD_LT( RAD1, RAD2 ) (ABS(RAD1) < ABS(RAD2))

/*****************************************************************************
 * Compute sine and cosine.
 * Example:
 * pair<fp,fp> si_co = SinCos<fp>( rad );
 * fp& s = si_co.first;
 * fp& c = si_co.second;
 *****************************************************************************/
template<typename FP>
std::pair<FP,FP> SinCos( const Radian rad )
{
#if !defined(__clang__) && defined(__GNUC__) && CPU_X86
    std::pair<FP,FP> si_co;
    asm("fsincos" : "=t" (si_co.second), "=u" (si_co.first) : "0" (rad));
    return si_co;
#elif !defined(__clang__) && defined(__GNUC__) && _GLIBCXX_HAVE_SINCOS
    std::pair<double,double> si_co;  // must be specific in this case
    sincos( rad, &si_co.first, &si_co.second );  // GNU extension
    return std::make_pair<FP,FP>( si_co.first, si_co.second );
#else
    std::pair<FP,FP> si_co;
    si_co.first  = std::sin( rad );
    si_co.second = std::cos( rad );
    return si_co;
#endif
}

/*****************************************************************************
 * Convert a (360) degree into a radian.
 * 360' = 2 * PI
 *****************************************************************************/
INTERN const fpx degRadRatio = (1.0 / 360.0) * (math::PI_DOUBLE);  // fpx
#define DEG2RAD( DEG ) ((DEG) * (degRadRatio))

INLINE Radian
Deg2Rad( const Degree deg )
{
    return DEG2RAD( deg );
    //return deg / 360.0 * (2.0 * math::PI);  // x87 FDIV is slower than FMUL
}

/*****************************************************************************
 * Convert a radian into a (360) degree.
 * 360' = 2 * PI
 *****************************************************************************/
INTERN const fpx radDegRatio = (1.0 / math::PI_DOUBLE) * 360.0;  // fpx
#define RAD2DEG( RAD ) ((RAD) * (radDegRatio))

INLINE Degree
Rad2Deg( const Radian rad )
{
    return RAD2DEG( rad );
    //return rad / (2.0 * math::PI) * 360.0;  // x87 FDIV is slower than FMUL
}

/*****************************************************************************
 * 360' sin().
 *****************************************************************************/
INLINE Degree::value_type
Sin( const Degree deg )
{
    return std::sin( Deg2Rad( deg ) );
}

/*****************************************************************************
 * 360' cos().
 *****************************************************************************/
INLINE Degree::value_type
Cos( const Degree deg )
{
    return std::cos( Deg2Rad( deg ) );
}

} // namespace math

#endif // MATH_FUNCS_TRIG_HH
